package com.frog.common.util;

import com.alibaba.fastjson.JSON;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author wuqiong 2022/3/5
 * @description
 */
public class HttpClientHelper {

    private static Logger logger = LoggerFactory.getLogger(HttpClientHelper.class);


    private static final String ENCODE = "UTF-8";
    // 从连接池获取连接的timeout
    private static final int connectionRequestTimeout = 3000;
    // 客户端和服务器建立连接的timeout
    private static final int connectionTimeout = 60000;
    // 客户端从服务器读取数据的timeout
    private static final int socketTimeout = 60000;

    // 用于创建普通的http连接
    private static HttpClientBuilder httpClientBuilder = null;
    static {
        httpClientBuilder = HttpClientBuilder.create();
        RequestConfig.Builder custom = RequestConfig.custom();
        custom.setConnectionRequestTimeout(connectionRequestTimeout)
                .setConnectTimeout(connectionTimeout)
                .setSocketTimeout(socketTimeout);
        httpClientBuilder.setDefaultRequestConfig(custom.build());
    }

    // 用于创建https连接
    private static HttpClientBuilder httpsClientBuilder = null;
    static {
        try {
            SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
            sslContextBuilder.loadTrustMaterial(new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            });
            SSLContext sslContext = sslContextBuilder.build();
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,new NoopHostnameVerifier());
            httpsClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
        } catch (Exception e) {
            logger.warn("[http-utils]创建https通道的httpClientBuilder失败，请检查:{}", e.getMessage());
            httpsClientBuilder = HttpClientBuilder.create();
        }
        RequestConfig.Builder custom = RequestConfig.custom();
        custom.setConnectionRequestTimeout(connectionRequestTimeout).setConnectTimeout(connectionTimeout)
                .setSocketTimeout(socketTimeout);
        httpsClientBuilder.setDefaultRequestConfig(custom.build());
    }


    /**
     * 发起一个get请求：
     *
     * <pre>
     *      当结果返回null时，有以下可能：
     * 	        1. 参数为空
     * 	        2. 返回的http状态码不是200
     *          3. 发生异常 调用端需要根据自身的业务规则去判断，当得到null时，要怎么继续接下来的业务逻辑
     * </pre>
     */
    public static String getRequest(String url, Map<String, String> headers) {
        if (url == null) {
            return null;
        }
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("User-Agent", "Java/1.8.0_312");
            if (headers != null) {
                Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> next = iterator.next();
                    httpGet.setHeader(next.getKey(), next.getValue());
                }
            }
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.warn("[http-utils]执行http请求时，返回结果不是200，url:{}, response:{}", url, JSON.toJSONString(response));
                return null;
            }
            String responseData = EntityUtils.toString(response.getEntity(), ENCODE);
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage(), e);
        }
        return null;
    }

    public static byte[] readAndGetBytes(String url, Map<String, String> headers) {
        if (url == null) {
            return null;
        }
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("User-Agent", "Java/1.8.0_312");
            if (headers != null) {
                Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> next = iterator.next();
                    httpGet.setHeader(next.getKey(), next.getValue());
                }
            }
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.warn("[http-utils]执行http请求时，返回结果不是200，url:{}, response:{}", url, JSON.toJSONString(response));
                return null;
            }
            byte[] responseData = EntityUtils.toByteArray(response.getEntity());
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage(), e);
        }
        return null;
    }

    public static byte[] writeAndGetBytes(String url, String data) {
        if (url == null || data == null) {
            return null;
        }
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(data, ENCODE);
            stringEntity.setContentEncoding(ENCODE);
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            HttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.warn("[http-utils]执行http请求时，返回结果不是200，url:{}", url);
                return null;
            }
            byte[] responseData = EntityUtils.toByteArray(response.getEntity());
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage());
        }
        return null;
    }


    /**
     *
     * @param url
     * @param data
     * @param headerMap
     * @return
     */
    public static String post(String url, String data, Map<String, String> headerMap) {
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        return post(url, data, selectClientBuilder, headerMap);
    }
    public static String post(String url, String data, HttpClientBuilder selectClientBuilder, Map<String, String> headerMap) {
        if (url == null || data == null) {
            return null;
        }
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(data, ENCODE);
            httpPost.setEntity(stringEntity);
            if (headerMap != null && !headerMap.isEmpty()) {
                headerMap.forEach(httpPost::setHeader);
            }
            HttpResponse postResponse = httpClient.execute(httpPost);
            if (postResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String errData = "";
                try {
                    String responseData = EntityUtils.toString(postResponse.getEntity(), ENCODE);
                    errData = responseData;
                } catch (Exception e) {
                    // 忽略异常，可能无法读取内容
                }
                logger.warn("[http-utils]执行http请求时，返回结果不是200，url:{},data:{}", url, errData);
                return null;
            }
            String responseData = EntityUtils.toString(postResponse.getEntity(), ENCODE);
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage());
        }
        return null;
    }

    public static String put(String url, String data, Map<String, String> headerMap) {
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        return put(url, data, selectClientBuilder, headerMap);
    }
    public static String put(String url, String data, HttpClientBuilder selectClientBuilder, Map<String, String> headerMap) {
        if (url == null || data == null) {
            return null;
        }
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpPut httpPut = new HttpPut(url);
            StringEntity stringEntity = new StringEntity(data, ENCODE);
            httpPut.setEntity(stringEntity);
            if (headerMap != null && !headerMap.isEmpty()) {
                headerMap.forEach(httpPut::setHeader);
            }
            HttpResponse response = httpClient.execute(httpPut);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String errData = "";
                try {
                    String responseData = EntityUtils.toString(response.getEntity(), ENCODE);
                    errData = responseData;
                } catch (Exception e) {
                    // 忽略异常，可能无法读取内容
                }
                logger.warn("[http-utils]执行http请求时，返回结果不是200，url:{},data:{}", url, errData);
                return null;
            }
            String responseData = EntityUtils.toString(response.getEntity(), ENCODE);
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage());
        }
        return null;
    }


    public static String write(String url, Map<String, String> data, Map<String, String> headerMap) {
        if (url == null || data == null) {
            return null;
        }
        HttpClientBuilder selectClientBuilder = getHttpClientBuilderByUrl(url);
        try (CloseableHttpClient httpClient = selectClientBuilder.build()) {
            HttpPost httpPost = new HttpPost(url);
            if (headerMap != null && !headerMap.isEmpty()) {
                headerMap.forEach(httpPost::setHeader);
            }
            List<NameValuePair> nameValuePairs = new ArrayList<>();
            Iterator<Map.Entry<String, String>> iterator = data.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, ENCODE));
            HttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.warn("[http-utils]执行http请求时，返回结果不是200,是:{}，url:{}", response.getStatusLine().getStatusCode(), url);
                return null;
            }
            String responseData = EntityUtils.toString(response.getEntity(), ENCODE);
            if (logger.isDebugEnabled()) {
                logger.debug("[http-utils]执行http请求时，返回， url: {}, data:{}", url, responseData);
            }
            return responseData;
        } catch (IOException e) {
            logger.warn("[http-utils]执行http请求时发生异常，url:{}, error:{}", url, e.getMessage());
        }
        return null;
    }


    private static HttpClientBuilder getHttpClientBuilderByUrl(String url) {
        HttpClientBuilder selectClientBuilder = null;
        if (url.startsWith("https://")) {
            selectClientBuilder = httpsClientBuilder;
        } else {
            selectClientBuilder = httpClientBuilder;
        }
        return selectClientBuilder;
    }

}
